﻿using DumbPrograms.ChromeDevTools;
using DumbPrograms.ChromeDevTools.Protocol.Database;
using DumbPrograms.ChromeDevTools.Protocol.DOM;
using DumbPrograms.ChromeDevTools.Protocol.Fetch;
using DumbPrograms.ChromeDevTools.Protocol.Input;
using log4net.Core;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using ZFBActivation.Core;
using ZFBActivation.Log;
using ZFBActivation.Model;
using ZFBActivation.ThreadPool;
using static System.Runtime.CompilerServices.RuntimeHelpers;
using static System.Runtime.InteropServices.JavaScript.JSType;


namespace ZFBActivation
{
	public class WordZFB :IDisposable
	{
		public MailPhone m_model { get; set; }
        public bool IsPost = false;

        protected InspectorClient chromeClient;
		protected ChromeHelper chromeHelper;

        private string jsqueryPwd = """var dom = document.querySelector('#queryPwd'); var evt = new InputEvent('input', { inputType: 'insertText', data: 'As1587652w', dataTransfer: null, isComposing: false}); dom.value = 'As1587652w'; dom.dispatchEvent(evt); var keyupEvent = new KeyboardEvent('keyup', { keyCode: 27, key: 'S',ctrlKey: false,  shiftKey: false,  altKey: false  });dom.dispatchEvent(keyupEvent);dom.blur()""";
        private string jspayPwd = """var dom = document.querySelector('#payPwd'); var evt = new InputEvent('input', { inputType: 'insertText', data: '858421', dataTransfer: null, isComposing: false}); dom.value = '858421'; dom.dispatchEvent(evt); var keyupEvent = new KeyboardEvent('keyup', { keyCode: 27, key: 'S',ctrlKey: false,  shiftKey: false,  altKey: false  });dom.dispatchEvent(keyupEvent);dom.blur()""";


        public async Task<bool> DoWork(ProxyInfo proxy) {

			try {

                //CloseChrome();

                IsPost = false;

                m_model.Msg = "启动浏览器";
                chromeHelper = new ChromeHelper();
                string proxyUrl = string.Empty;
                if (proxy != null) {
                    proxyUrl = $"http://{proxy.ip}:{proxy.port}";
                }
                int prot = GetFirstAvailablePort();

                chromeClient = await chromeHelper.CreatePage(new ChromeSet() { Width = 1433, Height = 1268, prot= prot,   Mode = OpenMode.有头并显示并加载图片,CacheKey= m_model.CacheKey, ProxyUrl= proxyUrl, });
                if(chromeClient == null) {
                    TBServer.Instance.SetState();
                    m_model.Msg = "启动浏览器失败";
                    return false;
                }
				await chromeClient.Fetch.Enable(new[] {
                    new DumbPrograms.ChromeDevTools.Protocol.Fetch.RequestPattern() {  UrlPattern = "https://memberprod.alipay.com/account/reg/regComplete.htm" }
                } );
                await chromeClient.Network.Enable();

                await chromeClient.Emulation.SetUserAgentOverride(RandomUSERAGENT());
                chromeClient.Fetch.RequestPaused += Fetch_RequestPaused;
                var resultResp =  await chromeClient.Page.Navigate(m_model.Url);
                 await chromeClient.WaitNavigateCompleted();

                await Task.Delay(1000);

                if (!string.IsNullOrEmpty(resultResp.ErrorText)) {
                    ErrorModel("无法打开站点,检查代理");
                    TBServer.Instance.SetState();
                    return false;
                }
                string html = await chromeClient.GetPageHtml();
                if (html.IndexOf("激活链接无效") > -1) {
                    ErrorModel("激活链接无效", true);
                    TBServer.Instance.SetState();
                    return false;
                }
                if (html.IndexOf("支付宝") <= -1) {
                    ErrorModel("无法打开站点,检查代理");
                    TBServer.Instance.SetState();
                    return false;
                }
                int delay = Random.Shared.Next(1200, 2000);
                await Task.Delay(delay);
                string sfz = GenerateSFZ.generate();
                string name = GetNameHelper.GetManName();

                m_model.Name = name;
                m_model.SFZCode = sfz;
                m_model.Msg = "填写表单";

                await chromeClient.ExecuteScriptAsync(jsqueryPwd);
                await chromeClient.ExecuteScriptAsync("document.querySelector(\"#queryPwdConfirm\").value = 'As1587652w'");

                await chromeClient.ExecuteScriptAsync(jspayPwd);
                await chromeClient.ExecuteScriptAsync("document.querySelector(\"#payPwdConfirm\").value='858421'");

                await chromeClient.ExecuteScriptAsync("document.querySelector(\"#realName\").focus()");
                await chromeClient.Input.InsertText(m_model.Name);

                await chromeClient.ExecuteScriptAsync("document.querySelector(\"#IDCardNo\").focus()");
                await chromeClient.Input.InsertText(m_model.SFZCode);

                var rect = await chromeClient.ExecuteScriptAsync("JSON.stringify(document.querySelector(\"#J-submit\").getBoundingClientRect())");
                var Jobj = rect.ToObject<JObject>();
                var x = Jobj.Value<double>("x") + Jobj.Value<double>("width") / 2 + Random.Shared.Next(-5, 5);
                var y = Jobj.Value<double>("y") + Jobj.Value<double>("height") / 2 + Random.Shared.Next(-5, 5);
                await chromeClient.Input.DispatchMouseEvent("mousePressed", x, y, button: DumbPrograms.ChromeDevTools.Protocol.Input.MouseButton.Left, clickCount: 1);
                await Task.Delay(Random.Shared.Next(20, 60));
                await chromeClient.Input.DispatchMouseEvent("mouseReleased", x, y, button: DumbPrograms.ChromeDevTools.Protocol.Input.MouseButton.Left, clickCount: 1);

                return true;
            }
            catch {
                TBServer.Instance.SetState();
            }
            return false;
        }


        public async Task<bool> CheckSuccess() {

            try {
                if (IsEnd) {
                    LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 注册已完成，不再检查");
                    return false;
                }
                m_model.Index++;

                m_model.Msg = $"检查是否成功{m_model.Index}";
                var html = await chromeClient.GetPageHtml();
                if (html.IndexOf("请重新填写账户信息") > -1) {
                    ErrorModel("操作异常，请重新填写账户信息",true);
                    LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 操作异常，请重新填写账户信息");
                    return false;
                }
                if (html.IndexOf("支付宝") <= -1) {
                    ErrorModel("无法打开站点,检查代理");
                    LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 检查中无法找到 支付宝 ");
                    return true;
                }

                var url = await chromeClient.ExecuteScriptAsync("window.location.href");
                if (url.IndexOf("regComplete.htm") > -1) {
                    LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 检查中");
                    return false;
                }

                if (html.IndexOf(m_model.Email.Trim()) > 0) {
                    LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 注册成功");
                    m_model.IsSuccess = true;
                    return true;
                }
                if(url.IndexOf("https://zht.alipay.com/asset/paymethod/paymethod.htm") > -1) {
                    LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 注册成功");
                    m_model.IsSuccess = true;
                    return true;
                }
                LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 啥也没检查到", url,html);

                return false;
            }
            catch (Exception ex) {
                LogHelper.Log(LogDefine.LogError, ex, "检查是否成功异常");
                ErrorModel("浏览器关闭");
                return false;
            }

        }
        public bool IsEnd { get; set; }

        public bool UpdateDbModel() {
            if (IsEnd) {
                LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 注册成功 已经更新数据库");
                CloseChrome();
                return true;
            }
            LogHelper.Log(LogDefine.LogVerbose, $"{m_model.Email} 注册成功 更新数据库");

            m_model.Msg = "注册成功";
            m_model.QueryPwd = SettingHelper.SysSetting.QueryPwd;
            m_model.PayPwd = SettingHelper.SysSetting.PayPwd;
            DbContext.DB.Update<MailPhone>()
                .Set(t => t.IsSuccess, m_model.IsSuccess).Set(t => t.Msg, m_model.Msg)
                .Set(t => t.PayPwd, m_model.PayPwd).Set(t => t.QueryPwd, m_model.QueryPwd)
                .Set(t => t.Name, m_model.Name).Set(t => t.SFZCode, m_model.SFZCode).Where(t => t.Email == m_model.Email).ExecuteAffrows();
            IsEnd = true;
            CloseChrome();
            return true;
        }

        private bool IsError = false;

        public void CloseChrome() {
            try {
                if (chromeClient != null) {
                    if (!chromeClient.IsClose) {
                        chromeClient.Fetch.RequestPaused -= Fetch_RequestPaused;
                        chromeClient.Dispose();
                        chromeClient = null;
                    }
                }
            }
            catch { }

            try {
                if (chromeHelper != null) {
                    chromeHelper.ChromeDispose();
                    chromeHelper = null;
                }
            }
            catch { }

        }



        public async Task InputString(string value) {
            try {
                int count = value.Length;
                KeyInputInfo keyInput = new KeyInputInfo();
                for (int i = 0; i < count; i++) {
                    keyInput.text = value[i].ToString();
                    await chromeClient.Input.DispatchKeyEvent("keyDown", keyInput.modifiers, text: keyInput.text);
                    await Task.Delay(Random.Shared.Next(5, 15));
                    await chromeClient.Input.DispatchKeyEvent("keyUp", keyInput.modifiers, text: keyInput.text);
                    await Task.Delay(Random.Shared.Next(5, 15));

                }
            }
            catch (Exception ex) {
             
            }
        }

        public static string RandomUSERAGENT() {
            int v1 = Random.Shared.Next(95, 97);
            int v2 = Random.Shared.Next(4538, 4638);
            int v3 = Random.Shared.Next(40, 80);
            return $"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{v1}.0.{v2}.{v3} Safari/537.36";
        }

        private void ErrorModel(string msg,bool IsInvalid = false) {
            if(IsInvalid) {
                m_model.IsInvalid = true;
            }
            IsEnd = true;
            m_model.Msg = msg;
            DbContext.DB.Update<MailPhone>().Set(t => t.IsInvalid, m_model.IsInvalid).Set(t => t.Msg, m_model.Msg).Where(t => t.Email == m_model.Email).ExecuteAffrows();
            CloseChrome();
        }

        public class KeyInputInfo
        {
            public string type { get; set; } = "char";
            public int modifiers { get; set; } = 0;
            public string text { get; set; }
        }
        //gotoPage=&userType=2&scene=enterpriseReg&modifyLoginPwd=1&queryPwd=AS220033&queryPwdConfirm=AS220033&payPwd=220033&payPwdConfirm=220033&securityQuestion=husbandName&otherSecurityQuestion=&securityAnswer=88

        //Protocol.Fetch.RequestPausedEvent
        private async Task Fetch_RequestPaused(RequestPausedEvent pausedEvent) {
            m_model.Msg = "拦截内容";

            PostDic postDic = new PostDic(pausedEvent.Request.PostData);


            postDic.Del("countryCode");
            postDic.Del("certValidTo");
            postDic.Del("certNo");
            postDic.Del("gender");
            postDic.Del("realName");


            postDic.Set("queryPwd",SettingHelper.SysSetting.QueryPwd);
            postDic.Set("queryPwdConfirm", SettingHelper.SysSetting.QueryPwd);
            postDic.Set("payPwd", SettingHelper.SysSetting.PayPwd);
            postDic.Set("payPwdConfirm", SettingHelper.SysSetting.PayPwd);


            postDic.Set("modifyLoginPwd", "1");
            postDic.Set("userType", "2");
            postDic.Set("scene", "enterpriseReg");
            postDic.Set("securityQuestion", "husbandName");
            postDic.Set("otherSecurityQuestion", "");
            postDic.Set("securityAnswer", SettingHelper.SysSetting.QueryPwd);

            string base64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(postDic.GetBuild()));

            pausedEvent.Request.PostData = postDic.GetBuild();

            TBServer.Instance.SetState();

            m_model.Msg = "等待其它窗口准备完成";

            await WaitAction(()=>TBServer.Instance.IsOK());

            m_model.Msg = "提交修改后内容";
            await chromeClient.Fetch.ContinueRequest(pausedEvent.RequestId, postData: base64);
        }



        public async ValueTask<bool> WaitAction(Func<bool> action, int timeOut = 999) {
            DateTime newTime = DateTime.Now;
            while (true && (DateTime.Now - newTime).TotalSeconds < timeOut) {
                var ok = action();
                if (ok) {
                    return true;
                }
                await Task.Delay(1);
            }
            return false;
        }
        public async ValueTask<bool> WaitAction(Func<ValueTask<bool>> action, int timeOut = 999) {
            DateTime newTime = DateTime.Now;
            while (true && (DateTime.Now - newTime).TotalSeconds < timeOut) {
                var ok = await action();
                if (ok) {
                    return true;
                }
                await Task.Delay(1000);
            }
            return false;
        }

        public void Dispose() {
            try {
                if (chromeClient != null) {
                    if (!chromeClient.IsClose) {
                        chromeClient.Fetch.RequestPaused -= Fetch_RequestPaused;
                        chromeClient.Dispose();
                    }
                }
            }
            catch { }

            if (chromeHelper != null) {
                chromeHelper.ChromeDispose();
            }
        }


        private static Dictionary<int, int> dic = new Dictionary<int, int>();
        private static readonly object obj = new object();

        public static int GetFirstAvailablePort() {

            lock (obj) {
                int MAX_PORT = 65535; //系统tcp/udp端口数最大是65535            
                int BEGIN_PORT = 5000;//从这个端口开始检测
                for (int i = BEGIN_PORT; i < MAX_PORT; i++) {
                    if (PortIsAvailable(i) && !dic.ContainsKey(i)) {
                        dic.Add(i, i);
                        return i;
                    }
                }
                return -1;
            }
        }
        /// <summary>
        /// 获取操作系统已用的端口号
        /// </summary>
        /// <returns></returns>
        private static IList PortIsUsed() {
            //获取本地计算机的网络连接和通信统计数据的信息
            IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();

            //返回本地计算机上的所有Tcp监听程序
            IPEndPoint[] ipsTCP = ipGlobalProperties.GetActiveTcpListeners();

            //返回本地计算机上的所有UDP监听程序
            IPEndPoint[] ipsUDP = ipGlobalProperties.GetActiveUdpListeners();

            //返回本地计算机上的Internet协议版本4(IPV4 传输控制协议(TCP)连接的信息。
            TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

            IList allPorts = new ArrayList();
            foreach (IPEndPoint ep in ipsTCP) allPorts.Add(ep.Port);
            foreach (IPEndPoint ep in ipsUDP) allPorts.Add(ep.Port);
            foreach (TcpConnectionInformation conn in tcpConnInfoArray) allPorts.Add(conn.LocalEndPoint.Port);
            return allPorts;
        }



        /// <summary>

        /// 检查指定端口是否已用
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        private static bool PortIsAvailable(int port) {
            bool isAvailable = true;
            IList portUsed = PortIsUsed();
            foreach (int p in portUsed) {
                if (p == port) {
                    isAvailable = false; break;
                }
            }
            return isAvailable;
        }
    }

}
